%!TEX encoding = UTF-8 Unicode

\documentclass[a4paper,11pt]{extarticle}
% L'option 'openany' permet de démarrer un chapitre sur une page paire

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   R É G L A G E S    « F R A N Ç A I S »                                                                              *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

%--- Paquetage pour le codage des sources en UTF-8
\usepackage[utf8]{inputenc}

%--- Latex demande ce paquetage pour mieux afficher le caractère "°" et \textquotesingle "'"
\usepackage{textcomp}

%--- Ce paquetage permet d'effectuer certaines césures, et ainsi d'éviter les messages "Overfull \hbox"
\usepackage[T1]{fontenc}

\usepackage{lmodern}

%--- Paquetage pour imposer les réglages français
%\usepackage[frenchb]{babel}

%------------------------------------------------------------------------------------------------
%   C H O I X    D E    L A    P O L I C E
%------------------------------------------------------------------------------------------------

\usepackage[scaled=0.9, default]{sourcecodepro}
%\usepackage{fouriernc}
\usepackage[default]{sourcesanspro}

\usepackage[dvipsnames]{xcolor}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   M I S E    E N    P A G E                                                                                           *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

% Voir "Une courte introduction à Latex2e", § 6.4

%--- Marge gauche : 2,54 cm ; le paramètre \hoffset contient cette valeur, moins 1 pouce
%    \hoffset = 2,54 cm - 2,54 cm = 0 cm
\setlength{\hoffset}{0.cm}

%--- Marges supplémentaires, différenciées pour les pages gauches et droites ; ici, aucune.
\setlength{\oddsidemargin }{0 cm}
\setlength{\evensidemargin}{0 cm}

%--- Largeur du texte
%    \textwidth = 210 mm - 25.4 mm - 25.4 mm = 15,4 cm
\setlength{\textwidth}{15.92 cm}

%--- Marge haute : 2,54 cm ; le paramètre \voffset contient cette valeur, moins 1 pouce
%    \voffset = 2,54 cm - 2,54 cm = 0 cm
\setlength{\voffset}{0 cm}

%--- Distance entre la marge haute et l'en-tête : 0 cm
\setlength{\topmargin}{0 cm}

%--- Hauteur de l'en-tête de chaque page : 1 cm
\setlength{\headheight}{1 cm}

%--- Distance entre l'en-tête de chaque page et le corps : 0,5 cm
\setlength{\headsep}{0.5 cm}

%--- Hauteur du corps
%    \textheight = 29,7 cm - 2,54 cm - 2,8 cm - 1,5 cm = 22,6 cm
\setlength{\textheight}{22.86 cm}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   E X T E N S I O N S    P O U R    L ' É C R I T U R E    D E S     F O R M U L E S    M A T H É M A T I Q U E S     *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

%--- Extensions pour l'écriture des formules mathématiques
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsfonts}

%--- Paquetage "IEEEtrantools"
% Pour créer des tableaux d'équations, bien alignées
% Voir courte-intro-latex.pdf, page §3.5.2 page 83
\usepackage[retainorgcmds]{IEEEtrantools}

%-----------------------------------------------------------------------------------------------------------------------*

\usepackage{mdframed}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   P A Q U E T A G E    « L I S T I N G S »                                                                            *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\usepackage{listings}

\lstset{
  basicstyle=\ttfamily\small,
  keywordstyle=\color{blue},
  commentstyle=\color{red},
  stringstyle=\color{orange},
  frame=l,
  backgroundcolor=\color{green!20},
  numberstyle=\ttfamily\small,
  xleftmargin=20pt
}

\lstdefinelanguage{py}{
  style=py,
  comment=[l]{\%},
}

\lstdefinestyle{py}{
  basicstyle=\ttfamily\small,
  backgroundcolor=\color{orange!25},
  numberstyle=\ttfamily\small,
  xleftmargin=15pt,
  frame=tl
}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   H Y P E R R E F                                                                                                     *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*


%--- Pour les hyperliens, et le contrôle de la génération PDF 
\usepackage[pagebackref]{hyperref}

\hypersetup{pdftitle      = {Python makefile}}
\hypersetup{pdfauthor     = {Pierre Molinaro}}

\hypersetup{colorlinks    = true}
\hypersetup{anchorcolor   = black}
\hypersetup{filecolor     = black}
\hypersetup{menucolor     = black}
\hypersetup{plainpages    = false}
\hypersetup{pdfstartview  = FitH}
\hypersetup{pdfpagelayout = OneColumn}
\hypersetup{linkcolor  = blue}
\hypersetup{urlcolor   = blue}
\hypersetup{citecolor  = blue}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   E N - T Ê T E S    E T    P I E D S    D E    P A G E S                                                             *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\usepackage{fancyhdr}
\pagestyle{fancy}

\usepackage{xcolor}

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   D É B U T    D U    D O C U M E N T                                                                                 *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\begin{document} 

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   T I T R E                                                                                                           *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\title{\bf \Huge{Python Makefile\\Version 3.3}}
\author{Pierre Molinaro}
\date {\today}

\maketitle

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   T A B L E    D E S    M A T I È R E S                                                                               *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\tableofcontents

%-----------------------------------------------------------------------------------------------------------------------*
%                                                                                                                       *
%   L E S    C H A P I T R E S                                                                                          *
%                                                                                                                       *
%-----------------------------------------------------------------------------------------------------------------------*

\section{Versions}
  \begin{tabular}{llp{10.5cm}}
    \textbf{Version} & \textbf{Date} & \textbf{Commentaire}\\
    1.0 & March 4, 2015 & Initial version \\
    2.0 & October 2, 2015 & Definition of more than one target for a rule (exemple at page \pageref{plusieursCibles})\\
    2.1 & October 5, 2015 & Added a dynamic checking of arguments type\\
        &                & Added a simulation of the \texttt{clean} target (page \pageref{simulationButClean})\\
    2.2 & October 24, 2015 & Replaced \texttt{subprocess.Popen} by \texttt{subprocess.call} in \texttt{runCommand}\\
        &                & Checks external tools exist by using the \texttt{find\_executable} function (see note on page \pageref{verifUtilitaire})\\
        &                & Added an optional boolean argument in \texttt{Make} class constructor to display the complete path of external tools (see notes on pages \pageref{logUtilityToolPath} and \pageref{logUtilityToolPath2})\\
    2.3 & April 16, 2016 & Added a progress bar display (see page \pageref{affichagePourcentage})\\
    3.0 & May 30, 2016 & Python 3 compatibility: the script can be used with Python 2 and Python 3\\
    3.1 & May 25, 2018 & Added tolerance in secondary dependency file syntax\\
    3.2 & December 16th, 2019 & Concurrency bug fixed\\
        &                     & \emph{post command} title display is aligned\\
    3.2 & November 19th, 2021 & Accelerated job construction\\
  \end{tabular}
\\~

Version 2.0 breaks previous versions scripts. The first argument of \texttt{makefile.Rule} must be a list. The lines modified to be compatible with version 2.0 and higher are commented with "\texttt{\# Release 2}".

%Le chapitre \hyperref[plusieursCibles]{page \pageref{plusieursCibles}} donne 

\section{Introduction}

The \texttt{makefile} module is designed to build easily Python scripts which are a replacement for traditional makefiles. The \texttt{makefile} class have many assets:

\begin{itemize}
  \item Dependencies rules description is simpler;
  \item Blanks in paths can be used freely;
  \item A full featured programming langage. 
\end{itemize}

In the following, we will give an example of usage of this class.

\section{An example}

The example consists of two C files and a C header file.

File \texttt{main.c} :
\lstinputlisting[language=C]{../../main.c}


File \texttt{myRoutine.h} :
\lstinputlisting[language=C]{../../myRoutine.h}


File \texttt{myRoutine.c} :
\lstinputlisting[language=C]{../../myRoutine.c}

Separate compilation is done by the commands:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
mkdir -p objects\\
gcc -c main.c -o objects/main.c.o\\
gcc -c myRoutine.c -o objects/myRoutine.c.o\\
gcc objects/main.c.o objects/myRoutine.c.o -o myRoutine
\end{mdframed}


The object files are stored in a directory \texttt{objects} that must be created if it does not exist.


\section{Construction of the Python script}

This script is contained in an executable file \texttt{build.py}, located in the same directory as the C source files. Its content is described step by step.

~\\The first step is to import the module \texttt{makefile}, and other useful modules.
\begin{lstlisting}[language=py]
import sys, os, makefile
\end{lstlisting}

~\\To work with relative paths, it sets the current directory, obtained from the argument $0$.
\begin{lstlisting}[language=py]
scriptDir = os.path.dirname (os.path.abspath (sys.argv[0]))
os.chdir (scriptDir)
\end{lstlisting}

~\\Then we create an object \texttt{make}, which is the object that centralizes implementation of construction. A string argument must be provided, it is the goal to be built. The precise definition of this goal is made later.
\begin{lstlisting}[language=py]
make = makefile.Make ("all")
\end{lstlisting}

{\bf Note.}\label{logUtilityToolPath} From version 2.2, the creation of the \texttt{make} has an optional second Boolean argument. If it is true, calling each command displays the full path of the utility called. By default, this optional argument is false.
\begin{lstlisting}[language=py]
make = makefile.Make ("all", True) # Displays the path of each command called
\end{lstlisting}

~\\To present a general solution, we build two lists, one containing the list of C files to compile, the other the list of object files to be linked. The second is initially empty, it is built by looping through the list of source files. 
\begin{lstlisting}[language=py]
sourceList = ["main.c", "myRoutine.c"]
objectList = []
for source in sourceList:
  ...
\end{lstlisting}

~\\In the body of the loop, we construct each rule corresponding to the compilation of a source file. We begin by defining the \emph{target} of the rule, which is here the object file built by compiling the source C. We also want to collect in \texttt{ObjectList}  the list of object files.
\begin{lstlisting}[language=py]
  object = "objects/" + source + ".o"
  objectList.append (object)
\end{lstlisting}

~\\Then we instantiate an object \texttt{rule}  specifying as first argument the list of target files of the rule. The second argument is optional, it is the title that is displayed when running the rule. By default, without the second argument, the text \texttt{Building}  followed by the name of the target is displayed.
\begin{lstlisting}[language=py]
  rule = makefile.Rule ([object], "Compiling " + source)  # Release 2
\end{lstlisting}
Please note that the target (here "[\texttt{object}]") must be a list of files. You can not put a name such as \texttt{all} or \texttt{clean} here: these names are \emph{goals} and their definition is described later in this document.

~\\Then dependencies are added. Here there is only one dependency, the source file. The variable \texttt{rule.mDependences} is a list of strings, initially empty.
\begin{lstlisting}[language=py]
  rule.mDependences.append (source)
\end{lstlisting}
Caution, do not add here the dependencies to the header files. We will see how to take them into account later.

~\\We now construct the command to be executed to build the target. The "\texttt{rule.mCommand}" variable is a list of strings, initially empty.
\begin{lstlisting}[language=py]
  rule.mCommand.append ("gcc")
  rule.mCommand += ["-c", source]
  rule.mCommand += ["-o", object]
\end{lstlisting}
It is essential to define a string for each argument. If for example we want to add the "\texttt{-O2}" option, writing \texttt{rule.mCommand.append("gcc -O2")} is a mistake, it is mandatory to write \texttt{rule.mCommand += ["gcc", "-O2"]}.

{\bf Note.}\label{verifUtilitaire}
From version 2.2, the existence of the tool called is verified. An error message is displayed if the tool is not found; For example, if you write \texttt{rule.mCommand.append("gccc")}, then the following error message is displayed:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{red}{\bf*** Cannot find~\textquotesingle gccc\textquotesingle~executable ***}
\end{mdframed}

~\\The construction of the rule is complete; we can now add it to the Makefile.
\begin{lstlisting}[language=py]
  make.addRule (rule)
\end{lstlisting}


~\\{\bf To summarize.} The construction of the rules governing the building from the C sources is:
\begin{lstlisting}[language=py]
sourceList = ["main.c", "myRoutine.c"]
objectList = []
for source in sourceList:
  object = "objects/" + source + ".o"
  objectList.append (object)
  rule = makefile.Rule ([object], "Compiling " + source) # Release 2
  rule.mDependences.append (source)
  rule.mCommand.append ("gcc")
  rule.mCommand += ["-c", source]
  rule.mCommand += ["-o", object]
  make.addRule (rule)
\end{lstlisting}
Note that the object files are stored in the \texttt{objects}. Maybe it does not exist, and \texttt{gcc} will not create it, triggering an error if it does not exist. In fact, there is no need to worry about it. Indeed, when a rule is to be enforced, our makefile implicitly creates the target file directory, if this directory does not exist.

~\\We will now build the rule related to the linking. The name of the executable file is defined by the variable \texttt{product}, and dependancies are object files listed in the variable \texttt{ObjectList}.
\begin{lstlisting}[language=py]
product = "myRoutine"
rule = makefile.Rule ([product], "Linking " + product) # Release 2
rule.mDependences += objectList
rule.mCommand += ["gcc"]
rule.mCommand += objectList
rule.mCommand += ["-o", product]
make.addRule (rule)
\end{lstlisting}

~\\As a check, it is possible to display the rules.
\begin{lstlisting}[language=py]
make.printRules ()
\end{lstlisting}

The three rules we have set are displayed:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf{-}{-}{-} Print 3 rules {-}{-}{-}}\\
\textcolor{OliveGreen}{\bf Target: "objects/main.c.o"}\\
\hspace*{1.2em}Dependence: "main.c"\\
\hspace*{1.2em}Command:  "gcc" "-c" "main.c" "-o" "objects/main.c.o"\\
\hspace*{1.2em}Title: "Compiling main.c"\\
\textcolor{OliveGreen}{\bf Target: "objects/myRoutine.c.o"}\\
\hspace*{1.2em}Dependence: "myRoutine.c"\\
\hspace*{1.2em}Command:  "gcc" "-c" "myRoutine.c" "-o" "objects/myRoutine.c.o"\\
\hspace*{1.2em}Title: "Compiling myRoutine.c"\\
\textcolor{OliveGreen}{\bf Target: "myRoutine"}\\
\hspace*{1.2em}Dependence: "objects/main.c.o"\\
\hspace*{1.2em}Dependence: "objects/myRoutine.c.o"\\
\hspace*{1.2em}Command:  "gcc" "objects/main.c.o" "objects/myRoutine.c.o" "-o" "myRoutine"\\
\hspace*{1.2em}Title: "Linking myRoutine"\\
\textcolor{blue}{\bf{-}{-}{-} End of print rule {-}{-}{-}}
\end{mdframed}

~\\We now define goals ("\emph{goals}"). By default, there is a goal named \texttt{clean}, which has no effect (later we will see how to configure it). In our example, let's define two goals:
\begin{itemize}
  \item \texttt{all}, that control the building of the executable (the one that was mentioned in the instantiation of the class \texttt{Make});
  \item \texttt{compile}, which controls the compilation of sources, without linking (not used for now).
\end{itemize}

\begin{lstlisting}[language=py]
make.addGoal ("all", [product], "Building all")
make.addGoal ("compile", objectList, "Compile C files")
\end{lstlisting}
Each goal is defined by three arguments:
\begin{itemize}
\item its name, which is later used to refer;
\item the list of files it builds;
\item its description as a character string.
\end{itemize}

~\\As a check, we can print a list of goals.
\begin{lstlisting}[language=py]
make.printGoals ()
\end{lstlisting}

We get:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf{-}{-}{-} Print 2 goals {-}{-}{-}}\\
\textcolor{OliveGreen}{\bf Goal: "compile"}\\
\hspace*{1.2em}Target: "objects/main.c.o"\\
\hspace*{1.2em}Target: "objects/myRoutine.c.o"\\
\hspace*{1.2em}Message: "Compile C files"\\
\textcolor{OliveGreen}{\bf Goal: "all"}\\
\hspace*{1.2em}Target: "myRoutine"\\
\hspace*{1.2em}Message: "Building all"\\
\textcolor{blue}{\bf{-}{-}{-} End of print goal {-}{-}{-}}
\end{mdframed}

~\\Finally, we will launch the construction of the goal that has been given when instantiating the class \texttt{Make}, that is to say, the aim \texttt{all} (the construction of goal \texttt{compile} is presented below).

\begin{lstlisting}[language=py]
make.runGoal (0, False)
\end{lstlisting}


This call contains two arguments:
\begin{itemize}
\item an integer value that specifies how many rules can be executed in parallel;
  \begin{itemize}
  \item a positive value defines the number of rules that can be executed in parallel; in particular, the value $1$ requires a sequential execution rules;
  \item the value $0$ tells the Makefile to take the number of processors on the machine where the script runs;
  \item a strictly negative value indicates the Makefile to take the number processors on the machine where the script runs, to which is added the opposite of the value; for example, $-2$ on a quad-core defines the execution of $4-(-2) = 6$ rules in parallel;
  \end{itemize}
\item a Boolean value that indicates whether the command regarding each rule should be displayed or not; Title of each rule is always displayed.
\end{itemize}

The script displays (note the creation of the directory \texttt{objects})

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf Making "objects" directory}\\
$[$~33\%]~\textcolor{blue}{\bf Compiling main.c}\\
$[$~66\%]~\textcolor{blue}{\bf Compiling myRoutine.c}\\
$[$100\%]~\textcolor{blue}{\bf Linking myRoutine}
\end{mdframed}

{\bf New in version 2.3.}\label{affichagePourcentage} The percentage that is displayed represents the percentage of completed commands when the command mentioned on the same line will be completed. If $N$ commands must be executed, for command $i$ ($1 \leqslant i \leqslant N$ ) the displayed value is $100 \times i / N$. Also, the last command is always preceded by "\ texttt {[100 \%]}".

If we do not want to display this information, simply call the method \texttt{doNotShowProgressString}  of class \texttt{Make} before calling \texttt{runGoal}:

\begin{lstlisting}[language=py]
make.runGoal (0, False)
make.doNotShowProgressString ()
\end{lstlisting}


If we relaunch immediately the script, the makefile examines the dependencies and reports that there is nothing to build:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf Nothing to make.}
\end{mdframed}



~\\Calling \texttt{runGoal} with the last argument to \texttt{True} displays the commands that are executed:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf Making "objects" directory}\\
mkdir -p objects\\
$[$~33\%]~\textcolor{blue}{\bf Compiling main.c}\\
gcc -c main.c -o objects/main.c.o\\
$[$~66\%]~\textcolor{blue}{\bf Compiling myRoutine.c}\\
gcc -c myRoutine.c -o objects/myRoutine.c.o\\
$[$100\%]~\textcolor{blue}{\bf Linking myRoutine}\\
gcc objects/main.c.o objects/myRoutine.c.o -o myRoutine
\end{mdframed}

~\\If an error is detected, such as when executing a rule that fails because of a compilation error, the execution of \texttt{runGoal}  waits for all parallel executions to finish. To view errors and drop the script by returning an error code, you can call:
\begin{lstlisting}[language=py]
make.printErrorCountAndExitOnError ()
\end{lstlisting}
Calling this method has no effect if there is no error.

\section{Dependencies on the header files}

In previous writings, we had not taken into account the dependencies on the header files for compiling C sources. In the example, these dependencies are very simple, there is only one user header file: \texttt{myRoutine.h}. One might add that file in a dependency list defined by \texttt{rule.mDependences}.

But in a real case, this requires to maintain yourself the list of dependencies for each source file. Or, depending on the evolution of the program, this list can change: the header files may be unnecessary, others can be forgotten. It is much easier and safer to entrust this task to GCC and to the Python makefile.

Automatically maintaining dependencies on header files is done in three steps.

First choose a name for the dependency file by selecting the name of the object file, to which is added the \texttt{dep}. This file will be placed in the \texttt{objects} directory.
\begin{lstlisting}[language=py]
  depObject = object + ".dep"
\end{lstlisting}

Then tell GCC to build it; We add the following compilation options:
\begin{lstlisting}[language=py]
  rule.mCommand += ["-MD", "-MP", "-MF", depObject]
\end{lstlisting}

Finally, we add to the rule the dependency to this file (you must place the instance of the class \texttt{Make} as second argument):
\begin{lstlisting}[language=py]
  rule.enterSecondaryDependanceFile (depObject, make)
\end{lstlisting}


~\\{\bf Summary.} The rules for compiling C sources thus become:
\begin{lstlisting}[language=py]
sourceList = ["main.c", "myRoutine.c"]
objectList = []
for source in sourceList:
  object = "objects/" + source + ".o"
  depObject = object + ".dep"
  objectList.append (object)
  rule = makefile.Rule ([object], "Compiling " + source) # Release 2
  rule.mDependences.append (source)
  rule.mCommand.append ("gcc")
  rule.mCommand += ["-c", source]
  rule.mCommand += ["-o", object]
  rule.mCommand += ["-MD", "-MP", "-MF", depObject]
  rule.enterSecondaryDependanceFile (depObject, make)
  make.addRule (rule)
\end{lstlisting}

Now, any modification of a header file will cause the compilation of the source files that directly or indirectly include it. This is called a \emph{secondary} dependency because the absence of the file does not prevent the execution of the rule. Conversely, the absence of the source file causes an error, because there is no way to build it.

The execution of the \texttt{enterSecondaryDependanceFile} method can take a long time, since the date of modification of all files listed in the \texttt{depObject} file is checked. However, if the goal \texttt{clean} is invoked, this checking is not done because it is unnecessary: the execution time of the method is then negligible.


\section{Priority between rules}

By default, the rules that are ready to be executed are in the order they were added by the \texttt{addRule}. Thus, in our example, we defined the list of source files:
\begin{lstlisting}[language=py]
sourceList = ["main.c", "myRoutine.c"]
\end{lstlisting}
If we execute the rules sequentially (call \texttt{runGoal} with the second argument equal to $1$), \texttt{main.c} will be always compiled before \texttt{myRoutine.c}.

This order can be changed by assigning a priority to a rule: just change the value of the integer field \texttt{mPriority}  of the rule. By default, this field is set to zero. You can assign a positive, negative or zero value, as long as it is an integer. The more this field has higher value, the more the rule is of high priority. At equal priority the order of insertion of the rule (the \ texttt {} addRule) defines the order: the first rule inserted will be executed first.

For example, to compile the files in descending order of size, we write (just before the call to \texttt{addRule}):
\begin{lstlisting}[language=py]
  rule.mPriority = os.path.getsize (scriptDir + "/" + source)
\end{lstlisting}






\section{Taking into account several goals}

We defined the goal \texttt{compile}, but without having called it so far. We'll see how we can write a new Python script that will call this goal.

First, we modify the beginning of the script \texttt{build.py}  as follows:
\begin{lstlisting}[language=py]
#--- Get goal as first argument
goal = "all"
if len (sys.argv) > 1 :
  goal = sys.argv [1]
#--- Get max parallel jobs
maxParallelJobs = 0 # 0 means use host processor count
if len (sys.argv) > 2 :
  maxParallelJobs = int (sys.argv [2])
#--- Build python makefile
make = makefile.Make (goal, maxParallelJobs == 1)
\end{lstlisting}

{\bf Note.}\label{logUtilityToolPath2} From version 2.2, \texttt{makefile.Make}  has an optional second Boolean argument. If it is true, calling each command displays the full path of the utility called. By default, this optional argument is false. By placing \texttt{1 == maxParallelJobs} as the second argument, it displays this path if the construction of the goals is performed sequentially, without parallelism.

Then we modify the end of the script \texttt{build.py}  as follows:
\begin{lstlisting}[language=py]
make.runGoal (maxParallelJobs, maxParallelJobs == 1)
\end{lstlisting}

These changes allow to call this script with different arguments, allowing to set the goal and the parallelism.

~\\For example, the script \texttt{compile.py} , which performs only a compilation is written as :
\lstinputlisting[language=py]{../../compile.py}


~\\Another example is the script \texttt{verbose-build.py}  which performs a compilation file by file. It clarifies the error display, in fact, parallel compilation may cause interlaced display error messages, making them difficult to understand.
\lstinputlisting[language=py]{../../verbose-build.py}







\section{Post commands}

It is possible to add a rule of \emph{post commands} that are executed one after the other after the execution of the associated command. A typical example is the application of the utility \texttt{strip} the executable produced by the linker.

We can not write a normal rule, because the dependency file is the same as the target file.

~\\To define a \emph{post command}, we first instantiate a new object of type \texttt{PostCommand}:
\begin{lstlisting}[language=py]
postCommand = makefile.PostCommand ("Stripping " + product)
\end{lstlisting}
The argument shall contain the title that will be displayed during the execution of the post command.

~\\Is then added a command to be executed:
\begin{lstlisting}[language=py]
postCommand.mCommand += ["strip", "-A", "-n", "-r", "-u", product]
\end{lstlisting}

~\\Finally, add the \emph{post command} to the list of post commands for the rule:
\begin{lstlisting}[language=py]
rule.mPostCommands.append (postCommand)
\end{lstlisting}


~\\{\bf Summary.} The rule defines the linking becomes:
\begin{lstlisting}[language=py]
product = "myRoutine"
rule = makefile.Rule ([product], "Linking " + product) # Release 2
rule.mDependences += objectList
rule.mCommand += ["gcc"]
rule.mCommand += objectList
rule.mCommand += ["-o", product]
postCommand = makefile.PostCommand ("Stripping " + product)
postCommand.mCommand += ["strip", "-A", "-n", "-r", "-u", product]
rule.mPostCommands.append (postCommand)
make.addRule (rule)
\end{lstlisting}

The script now displays:

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\tt\footnotesize
\textcolor{blue}{\bf Making "objects" directory}\\
$[$~33\%]~\textcolor{blue}{\bf Compiling main.c}\\
$[$~66\%]~\textcolor{blue}{\bf Compiling myRoutine.c}\\
$[$100\%]~\textcolor{blue}{\bf Linking myRoutine}\\
\hspace*{4em}\textcolor{blue}{\bf Stripping myRoutine}
\end{mdframed}







\section{Deleting targets on error}

If an error occurs when executing a command (eg due to a compiler error), the executable file, if it exists, is not removed: then we have an executable that does not match the state of the program compilation..

It's easy to enforce the deletion of the executable during a command execution error. Just put the attribute \texttt{mDeleteTargetOnError} of the rule that controls the linking to \texttt{True}:

\begin{lstlisting}[language=py]
rule.mDeleteTargetOnError = True
\end{lstlisting}
This technique can be used for any target.

~\\{\bf Summary.} The rule that defines the linking becomes:
\begin{lstlisting}[language=py]
product = "myRoutine"
rule = makefile.Rule ([product], "Linking " + product)  # Release 2
rule.mDeleteTargetOnError = True
rule.mDependences += objectList
rule.mCommand += ["gcc"]
rule.mCommand += objectList
rule.mCommand += ["-o", product]
postCommand = makefile.PostCommand ("Stripping " + product)
postCommand.mCommand += ["strip", "-A", "-n", "-r", "-u", product]
rule.mPostCommands.append (postCommand)
make.addRule (rule)
\end{lstlisting}







\section{The goal \texttt{clean}}

By default, the goal \texttt{clean} is set but has no effect. It's easy to add deletion of files and directories when the goal \texttt{clean} is executed.

In our example, we want the execution of goal \texttt{clean} removes :
\begin{itemize}
  \item the executable file ;
  \item the directory containing the generated object files.
\end{itemize}

~\\To remove the executable when executing the goal \texttt{clean}, simply add the rule defining the linking:

\begin{lstlisting}[language=py]
rule.deleteTargetFileOnClean ()
\end{lstlisting}


~\\{\bf Summary.} The rule that defines the linking becomes :
\begin{lstlisting}[language=py]
product = "myRoutine"
rule = makefile.Rule ([product], "Linking " + product)  # Release 2
rule.deleteTargetFileOnClean ()
rule.mDeleteTargetOnError = True
rule.mDependences += objectList
rule.mCommand += ["gcc"]
rule.mCommand += objectList
rule.mCommand += ["-o", product]
postCommand = makefile.PostCommand ("Stripping " + product)
postCommand.mCommand += ["strip", "-A", "-n", "-r", "-u", product]
rule.mPostCommands.append (postCommand)
make.addRule (rule)
\end{lstlisting}


~\\To remove the \texttt{objects} directory when executing the goal \texttt{clean}, simply add the rule defining the compilation of C sources:

\begin{lstlisting}[language=py]
  rule.deleteTargetDirectoryOnClean ()
\end{lstlisting}

~\\{\bf Summary.} The rules for compiling C sources thus become:
\begin{lstlisting}[language=py]
sourceList = ["main.c", "myRoutine.c"]
objectList = []
for source in sourceList:
  object = "objects/" + source + ".o"
  depObject = object + ".dep"
  objectList.append (object)
  rule = makefile.Rule ([object], "Compiling " + source) # Release 2
  rule.deleteTargetDirectoryOnClean ()
  rule.mDependences.append (source)
  rule.mCommand.append ("gcc")
  rule.mCommand += ["-c", source]
  rule.mCommand += ["-o", object]
  rule.mCommand += ["-MD", "-MP", "-MF", depObject]
  rule.enterSecondaryDependanceFile (depObject, make)
  rule.mPriority = os.path.getsize (scriptDir + "/" + source)
  make.addRule (rule)
\end{lstlisting}

~\\Now just add the script \texttt{clean.py}, calling the goal \texttt{clean}:
\lstinputlisting[language=py]{../../clean.py}


\subsection{Simulation of goal \texttt{clean}}\label{simulationButClean}

From version 2.1, it is possible to display the commands launched by the goal \texttt{clean} without running them. To do this, simply call the method \texttt{simulateClean}.

\begin{lstlisting}[language=py]
  make.simulateClean ()
\end{lstlisting}

This method must be called before calling \texttt{make.runGoal}  to be taken into account.

\section{Automatic opening of a source file on error}

It is possible to program the automatic opening with an external editor of a source file when an error occurs. Just add the rules concerned the line:
\begin{lstlisting}[language=py]
  rule.mOpenSourceOnError = True
\end{lstlisting}


We will begin with causing a compilation error by inserting a character « \S » at the 3rd line of file \texttt{myRoutine.h} :
\begin{lstlisting}[mathescape,language=C]
#ifndef MYROUTINE_DEFINED
#define MYROUTINE_DEFINED
$\S$
void myRoutine (void) ;

#endif
\end{lstlisting}

By running again the compilation, the error is detected (on a multi-heart, the two compilations occur in parallel):

\begin{mdframed}[hidealllines=true,backgroundcolor=lightgray!20]
\noindent\texttt{\footnotesize
$[$~33\%]~\textcolor{blue}{\bf Compiling myRoutine.c}\\
$[$~66\%]~\textcolor{blue}{\bf Compiling main.c}\\
In file included from main.c:1:\\
In file included from myRoutine.c:1:\\
./myRoutine.h:3:1: error: non-ASCII characters are not allowed outside of literals and identifiers\\
\S\\
\^\\
1 error generated.\\
./myRoutine.h:3:1: error: non-ASCII characters are not allowed outside of literals and identifiers\\
\S\\
\^\\
1 error generated.\\
\textcolor{red}{\bf Return code: 1}\\
Wait for job termination...\\
\textcolor{red}{\bf 1 error.}
}
\end{mdframed}

When the property \texttt{mOpenSourceOnError} is true and if an error occurs, the makefile research a file name at the beginning of line in the output stream of commands that caused an error . Specifically, it is based on the string that precedes the first occurrence of the character ":". This research provides four answers:

\begin{itemize}
\item \colorbox{lightgray!20}{\tt In file included from main.c}
\item \colorbox{lightgray!20}{\tt Compiling main.c}
\item \colorbox{lightgray!20}{\tt ./myRoutine.h}
\item \colorbox{lightgray!20}{\tt ./myRoutine.h}
\end{itemize}

The first two answers do not correspond to an existing file, they are ignored. The following two causes the opening of the \texttt{./myRoutine.h}  file with an external editor, which is by default:
\begin{itemize}
\item on Mac : \texttt{TextEdit} ;
\item on Linux : \texttt{gEdit}.
\end{itemize}

It's easy to change the default text editor, simply assign the following object  \texttt{make} properties :
\begin{itemize}
\item for Mac : \texttt{mMacTextEditor} ;
\item for Linux : \texttt{mLinuxTextEditor}.
\end{itemize}

For example, to use the editor \texttt{TextWrangler} on Mac:
\begin{lstlisting}[language=Py]
make.mMacTextEditor = "TextWrangler"
\end{lstlisting}





\section{Defining multiple targets for a rule}\label{plusieursCibles}

With version 2, several target files can be build by the execution of a rule. For example, we will modify the rule linker to build a text file "map" that contains the executable memory card. Three changes are made in the listing above (denoted \texttt{note 1}, \texttt{note 2} and \texttt{note3}).

\begin{lstlisting}[language=Py]
product = "myRoutine"
mapFile = product + ".map" # note1
rule = makefile.Rule ([product, mapFile], "Linking " + product) # Release 2, note2
rule.mDeleteTargetOnError = True
rule.deleteTargetFileOnClean ()
rule.mDependences += objectList
rule.mCommand += ["gcc"]
rule.mCommand += objectList
rule.mCommand += ["-o", product]
rule.mCommand += ["-Wl,-map," + mapFile] # note3
postCommand = makefile.PostCommand ("Stripping " + product)
postCommand.mCommand += ["strip", "-A", "-n", "-r", "-u", product]
rule.mPostCommands.append (postCommand)
make.addRule (rule)
\end{lstlisting}

{\bf note1.} The new target file \texttt{mapFile} is set here.

{\bf note2.} It is added to the target list constructed by the rule.

{\bf note3.} The command has been completed to build the new target file.
\\~

By running the goal \texttt{all}, both files \texttt{myRoutine} and \texttt{myRoutine.map} are built. If we remove one of the two files, running the goal \texttt{all} rebuild it. If we comment out the construction of the file \texttt{myRoutine.map}  (line commented by \texttt{note 3}), running the goal \texttt{all} displays an alert indicating that the file has not been built.











\section{The complete script \texttt{build.py}}

\lstinputlisting[language=py]{../../build.py}










\section{Other functions}

\subsection{Color printing}

The module \texttt{M
makefile} defines the following functions to make color prints:
\begin{itemize}
  \item \texttt{BLACK()}, \texttt{RED()}, \texttt{GREEN()}, \texttt{YELLOW()}, \texttt{BLUE()}, \texttt{MAGENTA()}, \texttt{CYAN()}, \texttt{WHITE()};
  \item \texttt{BOLD()}, \texttt{BLINK()}, \texttt{UNDERLINE()} ;
  \item \texttt{ENDC()}, which resets the print attributes.
\end{itemize}
All of these functions returns a string that can be directly used in a \texttt{print}:
\begin{lstlisting}[language=py]
print makefile.RED () + makefile.BOLD () + "Erreur !" + makefile.ENDC ()
\end{lstlisting}


\subsection{The method \texttt{enterError}}

The method \texttt{enterError} of class \texttt{Make} will display an error message and increment the error counter maintained by the receiver. It has a string argument characters, which is printed in bold red on the terminal.
\begin{lstlisting}[language=py]
make.enterError ("There is an error somewhere !")
\end{lstlisting}
Recall that a non-zero error counter inhibits the execution of rules when \texttt{runGoal}  is invoked.

\subsection{The method \texttt{errorCount}}

The method \texttt{errorCount} of class \texttt{Make} returns the current value of the error counter maintained by the receiver.
\begin{lstlisting}[language=py]
nombre_erreurs = make.errorCount ()
\end{lstlisting}


\subsection{The method \texttt{printErrorCount}}

The method \texttt{printErrorCount}  of class \texttt{Make} prints the current value of the error counter maintained by the receiver if it is not zero. If it is zero, nothing is printed.
\begin{lstlisting}[language=py]
make.printErrorCount ()
\end{lstlisting}


\subsection{Search for a file in a directory tree}

The method \texttt{searchFileInDirectories} of class \texttt{Make} searches a file in a directory list:
\begin{lstlisting}[language=py]
path = make.searchFileInDirectories (file, list_of_directories)
\end{lstlisting}
Where :
\begin{itemize}
  \item \texttt{file} is a character string that defines the name of the desired file ;
  \item \texttt{list\_of\_directories} is a list of strings that defines the list of directories in which the file will be searched ;
\end{itemize}
the method returns :
\begin{itemize}
  \item if \texttt{file} is found exactly once, the file name prefixed by the directory name that contains the file ;
  \item the empty string if the file is not found or if it is found several times; an error message is displayed, and the error count maintained by the receiver is incremented.
\end{itemize}

Recall that a non-zero error counter inhibits the execution of rules when \texttt{runGoal}  is invoked.

%-----------------------------------------------------------------------------------------------------------------------*
%   F I N    D U    D O C U M E N T                                                                                     *
%-----------------------------------------------------------------------------------------------------------------------*

\end{document}
